@tool
class_name Skill
extends Node2D

#region 属性
@export var skill_name:String:
	get():
		return name if not skill_name else skill_name
@export var skill_desc:String
# 技能优先级
@export var priority:int = 0
# 技能优先级冲突处理
@export var priority_conflict_resolution:GameEnum.SkillPriorityConflictResolution = GameEnum.SkillPriorityConflictResolution.Ignore
# 技能冷却时间
@export_range(0,1000,0.1) var skill_cooling_time:float
# 释放条件
@export var release_condition:Condition
# 释放消耗
@export var release_cost:Cost
@export var action:SkillAction:
	get:
		if not action:
			action = get_child(0)
		return action
@export var init_data:Dictionary[String,Variant]
@export var skill_event_infos:Array[SkillEventInfo]
var agent:SkillAgent
var skill_info:SkillInfo
var skill_state:GameEnum.SkillState = GameEnum.SkillState.Init
var cooling_timer:CustomTimer
var skill_event_info_dict:Dictionary[SkillEventInfo.SkillEventTiming,SkillEventInfo] = {}
var connect_obj_dict:Dictionary[Signal,ConnectObj] = {}
#endregion
#region 字段
var cur_blackboard:Blackboard:
	get:
		return action.cur_blackboard if action else null
var is_releasing:bool:
	get:
		return skill_state == GameEnum.SkillState.Releasing
#endregion
#region 信号
# 技能首次添加时
signal skill_init(skill:Skill)
# 技能释放时
signal skill_release(skill:Skill)
# 技能释放中（非物理更新）
signal skill_releasing_process(skill:Skill)
# 技能释放中（物理更新）
signal skill_releasing_physics_process(skill:Skill)
# 技能释放完成时
signal skill_release_completed(skill:Skill)
# 技能被打断时
signal skill_interrupted(skill:Skill)
# 技能进入冷却时
signal skill_cool(skill:Skill)
# 技能冷却中（非物理更新）
signal skill_cooling_process(skill:Skill)
# 技能冷却中（物理更新）
signal skill_cooling_physics_process(skill:Skill)
# 技能冷却完成时
signal skill_cool_completed(skill:Skill)
# 技能销毁时
signal skill_destroyed(skill:Skill)
# 输入
signal skill_unhandled_input(skill:Skill,event:InputEvent)
signal skill_unhandled_key_input(skill:Skill,event:InputEvent)
#endregion

#region 基类方法
#endregion
#region 公共方法
func init(args:Dictionary = {}):
	#var init_pos = args.get(SkillBlackboardKey.InitPos)
	#if init_pos:
		#global_position = init_pos
	#var init_dir = args.get(SkillBlackboardKey.InitDir)
	#if init_dir:
		#look_at(global_position + init_dir)
	#action = Utils.find_child_by_type(self,SkillAction)
	if skill_state != GameEnum.SkillState.Init:
		return
	agent = args.get(SkillBlackboardKey.Agent)
	skill_info = args.get(SkillBlackboardKey.SkillInfo)
	skill_event_info_dict = {}
	for skill_event_info in skill_event_infos:
		skill_event_info_dict.set(skill_event_info.skill_event_timing,skill_event_info)
		Utils.array_call_method(skill_event_info.additional_effects,"init",{Target = agent.entity,Owner = self})
	if action:
		init_data.set(SkillBlackboardKey.Agent,agent)
		init_data.set(SkillBlackboardKey.Skill,self)
		action.init(-1,init_data)
		if not connect_obj_dict.has(action.skill_action_release_completed):
			connect_obj_dict.set(action.skill_action_release_completed,ConnectObj.new(action.skill_action_release_completed,[]))
		var connect_obj:ConnectObj = connect_obj_dict.get(action.skill_action_release_completed) as ConnectObj
		connect_obj.connect_single(on_action_release_completed)
		if not connect_obj_dict.has(action.skill_action_interrupted):
			connect_obj_dict.set(action.skill_action_interrupted,ConnectObj.new(action.skill_action_interrupted,[]))
		connect_obj = connect_obj_dict.get(action.skill_action_interrupted) as ConnectObj
		connect_obj.connect_single(on_action_interrupted)
	set_update(false)
	skill_state = GameEnum.SkillState.Waiting
	var skill_event_info:SkillEventInfo = skill_event_info_dict.get(SkillEventInfo.SkillEventTiming.Init)
	if skill_event_info:
		Utils.array_call_method(skill_event_info.additional_effects,"excute")
	skill_init.emit(self)
## 释放技能
func release(args:Dictionary = {}):
	if skill_state != GameEnum.SkillState.Waiting:
		return
	if release_cost:
		release_cost.do_cost(agent.entity)
	if action:
		action.release(args)
	set_update(true)
	skill_state = GameEnum.SkillState.Releasing
	var skill_event_info:SkillEventInfo = skill_event_info_dict.get(SkillEventInfo.SkillEventTiming.Release)
	if skill_event_info:
		Utils.array_call_method(skill_event_info.additional_effects,"excute")
	skill_release.emit(self)
## 打断技能
func interrupt():
	if not is_releasing:
		return
	if action:
		action.interrupt()
	set_update(false)
	skill_state = GameEnum.SkillState.Interrupted
	var skill_event_info:SkillEventInfo = skill_event_info_dict.get(SkillEventInfo.SkillEventTiming.Interrupted)
	if skill_event_info:
		Utils.array_call_method(skill_event_info.additional_effects,"excute")
	skill_interrupted.emit(self)
	cool()
## 完成技能
func complete():
	if not is_releasing:
		return
	if action:
		action.complete()
	set_update(false)
	skill_state = GameEnum.SkillState.Completed
	var skill_event_info:SkillEventInfo = skill_event_info_dict.get(SkillEventInfo.SkillEventTiming.ReleaseCompleted)
	if skill_event_info:
		Utils.array_call_method(skill_event_info.additional_effects,"excute")
	skill_release_completed.emit(self)
	cool()
## 冷却技能
func cool():
	if skill_state != GameEnum.SkillState.Interrupted and skill_state != GameEnum.SkillState.Completed:
		return
	if action:
		action.cool()
	set_update(true)
	skill_state = GameEnum.SkillState.Cooling
	var skill_event_info:SkillEventInfo = skill_event_info_dict.get(SkillEventInfo.SkillEventTiming.Cool)
	if skill_event_info:
		Utils.array_call_method(skill_event_info.additional_effects,"excute")
	skill_cool.emit(self)
	if cooling_timer:
		GameCustomTimerSystem.remove_timer(cooling_timer.id)
		cooling_timer = null
	if skill_cooling_time > 0:
		cooling_timer = GameCustomTimerSystem.create_timer(skill_cooling_time)
		cooling_timer.start_timer()
		cooling_timer.register_listener(cooling_timer.timer_finished,func(timer_result:CustomTimer.TimerResult):
			cool_complete()
			)
	else:
		cool_complete()
## 冷却技能完成
func cool_complete():
	if cooling_timer:
		GameCustomTimerSystem.remove_timer(cooling_timer.id)
		cooling_timer = null
	set_update(false)
	skill_state = GameEnum.SkillState.Waiting
	var skill_event_info:SkillEventInfo = skill_event_info_dict.get(SkillEventInfo.SkillEventTiming.CoolCompleted)
	if skill_event_info:
		Utils.array_call_method(skill_event_info.additional_effects,"excute")
	skill_cool_completed.emit(self)
## 重置技能冷却
func reset_cool():
	if skill_state != GameEnum.SkillState.Cooling:
		return
	cool_complete()
## 销毁技能
func destroy():
	interrupt()
	if action:
		action.destroy()
	set_update(false)
	skill_state = GameEnum.SkillState.Destroyed
	var skill_event_info:SkillEventInfo = skill_event_info_dict.get(SkillEventInfo.SkillEventTiming.Destroyed)
	if skill_event_info:
		Utils.array_call_method(skill_event_info.additional_effects,"excute")
	queue_free()
	skill_destroyed.emit()
## 判断该技能能否释放
func check_can_release() -> GameEnum.SkillReleaseStatus:
	if action:
		if skill_state == GameEnum.SkillState.Init:
			return GameEnum.SkillReleaseStatus.NoInited
		elif skill_state == GameEnum.SkillState.Releasing:
			return GameEnum.SkillReleaseStatus.Releasing
		elif skill_state == GameEnum.SkillState.Cooling:
			return GameEnum.SkillReleaseStatus.Cooling
	if release_condition != null and not release_condition.check_result(agent.entity):
		return GameEnum.SkillReleaseStatus.DontMeetTheReleaseCondition
	if release_cost != null and not release_cost.check_cost(agent.entity):
		return GameEnum.SkillReleaseStatus.DontMeetTheReleaseCost
	return GameEnum.SkillReleaseStatus.Success
func set_update(enable:bool):
	set_process(enable)
	set_physics_process(enable)
func on_action_release_completed(skill_action:SkillAction,release_result:GameEnum.SkillReleaseResult):
	complete()
func on_action_interrupted(skill_action:SkillAction):
	interrupt()
#region SkillEditor
func get_clone() -> Skill:
	var clone:Skill = duplicate()
	clone.release_condition = release_condition.duplicate(true) if release_condition != null else null
	clone.init_data = init_data.duplicate(true) if init_data != null else null
	clone.action = action.get_clone() if action != null else null
	return clone
func get_export_property_name_list() -> Array[String]:
	var list:Array[String] = []
	list.append_array([
		"skill_name","skill_desc","priority","priority_conflict_resolution",
		])
	return list
func get_connection_info() -> Dictionary:
	var args = {}
	args.set("parent_max_connections",999)
	var parent_port_allow_types = [] as Array[GameEnum.SkillNodeType]
	parent_port_allow_types.append_array(GameEnum.SkillNodeType.values())
	args.set("parent_port_allow_types",parent_port_allow_types)
	args.set("children_max_connections",999)
	var children_port_allow_types = [] as Array[GameEnum.SkillNodeType]
	children_port_allow_types.append_array(GameEnum.SkillNodeType.values())
	args.set("children_port_allow_types",children_port_allow_types)
	return args
#endregion
#endregion
#region 私有方法
#endregion
#region 生命周期函数
func _process(delta: float) -> void:
	if skill_state == GameEnum.SkillState.Releasing:
		var skill_event_info:SkillEventInfo = skill_event_info_dict.get(SkillEventInfo.SkillEventTiming.ReleasingProcess)
		if skill_event_info:
			Utils.array_call_method(skill_event_info.additional_effects,"excute")
		skill_releasing_process.emit(self)
	elif skill_state == GameEnum.SkillState.Cooling:
		var skill_event_info:SkillEventInfo = skill_event_info_dict.get(SkillEventInfo.SkillEventTiming.CoolingProcess)
		if skill_event_info:
			Utils.array_call_method(skill_event_info.additional_effects,"excute")
		skill_cooling_process.emit(self)
func _physics_process(delta: float) -> void:
	if skill_state == GameEnum.SkillState.Releasing:
		var skill_event_info:SkillEventInfo = skill_event_info_dict.get(SkillEventInfo.SkillEventTiming.ReleasingPhysicsProcess)
		if skill_event_info:
			Utils.array_call_method(skill_event_info.additional_effects,"excute")
		skill_releasing_physics_process.emit(self)
	elif skill_state == GameEnum.SkillState.Cooling:
		var skill_event_info:SkillEventInfo = skill_event_info_dict.get(SkillEventInfo.SkillEventTiming.CoolingPhysicsProcess)
		if skill_event_info:
			Utils.array_call_method(skill_event_info.additional_effects,"excute")
		skill_cooling_physics_process.emit(self)
func _unhandled_input(event: InputEvent) -> void:
	var skill_event_info:SkillEventInfo = skill_event_info_dict.get(SkillEventInfo.SkillEventTiming.UnhandledInput)
	if skill_event_info:
		Utils.array_call_method(skill_event_info.additional_effects,"excute")
	skill_unhandled_input.emit(self,event)
func _unhandled_key_input(event: InputEvent) -> void:
	var skill_event_info:SkillEventInfo = skill_event_info_dict.get(SkillEventInfo.SkillEventTiming.UnhandledKeyInput)
	if skill_event_info:
		Utils.array_call_method(skill_event_info.additional_effects,"excute")
	skill_unhandled_key_input.emit(self,event)
func _notification(what: int) -> void:
	if what == NOTIFICATION_PREDELETE:
		for connect_obj:ConnectObj in connect_obj_dict.values():
			connect_obj.disconnect_all()
		connect_obj_dict.clear()
func _get_configuration_warnings() -> PackedStringArray:
	var warnings = []
	if get_child_count() != 1:
		warnings.append("此节点必须且只能有一个节点，类型必须是SkillAction")
	return warnings
#endregion
